﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media.Media3D;
using Eniux.Windows.ThreeDEngine.Helper;
using System.Windows.Input;

namespace Eniux.Windows.ThreeDEngine.Visual3Ds
{
    /// <summary>
    /// 旋转操纵器
    /// </summary>
    public class RotateManipulator : Manipulator
    {

        #region 变量

        /// <summary>
        ///  最后操作点
        /// </summary>
        private Point3D lastPoint;

        #endregion

        #region 注册属性

        #region Axis

        /// <summary>
        ///  旋转轴属性
        /// </summary>
        public static readonly DependencyProperty AxisProperty = DependencyProperty.Register(
            "Axis",
            typeof(Vector3D),
            typeof(RotateManipulator),
            new UIPropertyMetadata(new Vector3D(0, 0, 1), GeometryChanged));

        /// <summary>
        /// 获取或设置旋转轴
        /// </summary>
        public Vector3D Axis
        {
            get { return (Vector3D)GetValue(AxisProperty); }
            set { SetValue(AxisProperty, value); }
        } 

        #endregion

        #region Diameter

        /// <summary>
        ///  直径属性
        /// </summary>
        public static readonly DependencyProperty DiameterProperty = DependencyProperty.Register(
            "Diameter", typeof(double), typeof(RotateManipulator), new UIPropertyMetadata(3.0, GeometryChanged));

        /// <summary>
        /// 获取或设置直径
        /// </summary>
        public double Diameter
        {
            get { return (double)GetValue(DiameterProperty); }
            set { SetValue(DiameterProperty, value); }
        } 
        #endregion

        #region InnerDiameter

        /// <summary>
        ///  内径属性
        /// </summary>
        public static readonly DependencyProperty InnerDiameterProperty = DependencyProperty.Register(
            "InnerDiameter", typeof(double), typeof(RotateManipulator), new UIPropertyMetadata(2.5, GeometryChanged));

        /// <summary>
        /// 获取或设置内径属性
        /// </summary>
        public double InnerDiameter
        {
            get { return (double)GetValue(InnerDiameterProperty); }
            set { SetValue(InnerDiameterProperty, value); }
        } 
        #endregion

        #region Length

        /// <summary>
        ///  长度属性
        /// </summary>
        public static readonly DependencyProperty LengthProperty = DependencyProperty.Register(
            "Length", typeof(double), typeof(RotateManipulator), new UIPropertyMetadata(0.1, GeometryChanged));

        /// <summary>
        /// 获取或设置长度
        /// </summary>
        public double Length
        {
            get { return (double)GetValue(LengthProperty); }
            set { SetValue(LengthProperty, value); }
        } 
        #endregion

        #endregion

        #region 构造、析构

        /// <summary>
        /// 默认构造函数
        /// </summary>
        public RotateManipulator()
        {
            this.model = new GeometryModel3D();
            this.Visual3DModel = this.model;
            this.OnGeometryChanged();
        }

        #endregion

        #region 方法

        /// <summary>
        /// 重写模型改变事件
        /// </summary>
        protected override void OnGeometryChanged()
        {
            var mb = new MeshBuilder();
            var p0 = new Point3D(0, 0, 0);
            Vector3D d = this.Axis;
            d.Normalize();
            Point3D p1 = p0 - d * this.Length * 0.5;
            Point3D p2 = p0 + d * this.Length * 0.5;
            mb.AddPipe(p1, p2, this.InnerDiameter, this.Diameter, 60);

            // if (this.debugPoint != null)
            // {
            // mb.AddSphere(this.debugPoint.Value, 0.2);
            // }
            this.model.Geometry = mb.ToMesh();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseDown(MouseButtonEventArgs e)
        {
            base.OnMouseDown(e);
            Point3D hitPlaneOrigin = this.ToWorld(this.Position);
            Vector3D hitPlaneNormal = this.ToWorld(this.Axis);
            Point p = e.GetPosition(this.ParentViewport);

            Point3D? hitPoint = this.GetHitPlanePoint(p, hitPlaneOrigin, hitPlaneNormal);
            if (hitPoint != null)
            {
                this.lastPoint = this.ToLocal(hitPoint.Value);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (this.IsMouseCaptured)
            {
                Point3D hitPlaneOrigin = this.ToWorld(this.Position);
                Vector3D hitPlaneNormal = this.ToWorld(this.Axis);

                Point position = e.GetPosition(this.ParentViewport);
                Point3D? hitPoint = this.GetHitPlanePoint(position, hitPlaneOrigin, hitPlaneNormal);
                if (hitPoint == null)
                {
                    return;
                }

                Point3D currentPoint = this.ToLocal(hitPoint.Value);

                Vector3D v = this.lastPoint - this.Position;
                Vector3D u = currentPoint - this.Position;
                v.Normalize();
                u.Normalize();

                Vector3D currentAxis = Vector3D.CrossProduct(u, v);
                double sign = -Vector3D.DotProduct(this.Axis, currentAxis);
                double theta = Math.Sign(sign) * Math.Asin(currentAxis.Length) / Math.PI * 180;
                this.Value += theta;

                if (this.TargetTransform != null)
                {
                    var rotateTransform = new RotateTransform3D(new AxisAngleRotation3D(this.Axis, theta));
                    this.TargetTransform = Transform3DHelper.CombineTransform(rotateTransform, this.TargetTransform);

                    // this.Transform = Transform3DHelper.CombineTransform(rotateTransform, this.Transform);
                }

                hitPoint = this.GetHitPlanePoint(position, hitPlaneOrigin, hitPlaneNormal);
                if (hitPoint != null)
                {
                    this.lastPoint = this.ToLocal(hitPoint.Value);
                }
            }
        }

        #endregion
    }
}
